/*
 * Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>
#include <vector>
#include "cppv2.h"
#include "MxBase/Log/Log.h"
#include "opencv2/opencv.hpp"

#define USE_DVPP // if use opencv, this options should disable
namespace {
        const uint32_t YOLOV3_RESIZE = 416;
}

void InitV2Param(V2Param &v2Param)
{
    v2Param.deviceId = 0;
    v2Param.labelPath = "./model/yolov3.names";
    v2Param.configPath = "./model/yolov3_tf_bs1_fp16.cfg";
    v2Param.modelPath = "./model/yolov3_tf_bs1_fp16.om";
};

int main(int argc, char* argv[])
{
    if (argc <= 1) 
    {
        LogWarn << "Please input image path, such as './cppv2_sample test.jpg'.";
        return APP_ERR_OK;
    }
    APP_ERROR ret;
    
    // 初始化模型推理
    V2Param v2Param;
    InitV2Param(v2Param);
    auto yolov3 = std::make_shared<YoloV3Cppv2>(v2Param);

    // 读取图片
    std::string imgPath = argv[1];
 #ifdef USE_DVPP
    MxBase::Image decodedImage;

    ret = yolov3->ReadImage(imgPath, decodedImage);
    if (ret != APP_ERR_OK) {
        LogError << "YoloV3Cppv2 ReadImage failed, ret=" << ret << ".";
        return ret;
    }

    // 缩放图片
    MxBase::Image resizeImage;
    ret = yolov3->Resize(decodedImage, resizeImage);
    if (ret != APP_ERR_OK) {
        LogError << "YoloV3Cppv2 Resize failed, ret=" << ret << ".";
        return ret;
    }
    // 模型推理
    std::vector<MxBase::Tensor> yoloV3Outputs;
    ret = yolov3->Infer(resizeImage, yoloV3Outputs);
    if (ret != APP_ERR_OK) {
        LogError << "YoloV3Cppv2 Infer failed, ret=" << ret << ".";
        return ret;
    }

#else // use opencv
    std::string imgPath = argv[1];
    // opencv读取
    cv::Mat srcImg = imread(imgPath, cv::IMREAD_COLOR);
    if (srcImg.data == nullptr)
    {
        LogError << "opencv readImage failed";
    }
    cv::cvtColor(srcImg, srcImg, cv::COLOR_BGR2RGB);    // 需要和模型输入相同，此处对应RGB模型（本样例中使用了AIPP的YOLOV3模型为YUV输入，因此直接使用无推理结果）

    //opecnv缩放
    cv::Mat dstImg;
    cv::resize(srcImg, dstImg, cv::Size(YOLOV3_RESIZE, YOLOV3_RESIZE));

/*
    使用opencv进行crop示例
    static cv::Rect rectOfImg(x1, y1, x2, y2)
*/
    // cv::mat to Tensor
    std::vector<uint32_t> shape;
    shape.push_back(1); //batchSize
    shape.push_back(dstImg.rows);
    shape.push_back(dstImg.cols);
    shape.push_back(dstImg.channels());

    MxBase::TensorDType tensorDataType = MxBase::TensorDType::UINT8;
    MxBase::Tensor tensorInput(dstImg.data, shape, tensorDataType); 

    tensorInput.ToDevice(v2Param.deviceId); // !!!!!重要，使用外部数据作为tensor时务必使用to_device进行转移，缺失该步骤会导致输出结果异常，RC3以上版本已修复
    std::vector<MxBase::Tensor> yoloV3Inputs = {};
    yoloV3Inputs.push_back(tensorInput);

    // 模型推理
    std::vector<MxBase::Tensor> yoloV3Outputs;
    ret = yolov3->tensorInfer(yoloV3Inputs, yoloV3Outputs);
    if (ret != APP_ERR_OK) {
        LogError << "YoloV3Cppv2 Infer failed, ret=" << ret << ".";
        return ret;
    }
#endif

    // 模型后处理
    std::vector<MxBase::Rect> cropConfigVec;
    yolov3->YoloV3PostProcess(v2Param.configPath, v2Param.labelPath, yoloV3Outputs, cropConfigVec);

    return APP_ERR_OK;
};